#include "VideoDecoder.h"

#include "../inc/Utils.h"
#include "NormalFileDecoder.h"
#include "PSFileDecoder.h"
#include "ESDecoder.h"
#include "PSDecoder.h"
#include "DHFileDecoder.h"
#include "DHDecoder.h"
#include "RTSPDecoder.h"

void* DecodeThread(void* arg);

void* CaptureThread(void* arg);


using namespace video_decoder;


void* DecodeThread(void* arg)
{
	VideoDecoder* instance = (VideoDecoder*)arg;
	instance->startDecodeThread();
	return 0;
}

void* RecvThread(void* arg)
{
	VideoDecoder* instance = (VideoDecoder*)arg;
	instance->startRecvThread();
	return 0;
}

VideoDecoder::VideoDecoder(void)
{
	frameCount = 0;

	width = 0;
	height = 0;

	progress = 0;
	operationFlag = -1;
	playFlag = PAUSE;

	decodeThreadRunning = true;
	recvThreadRunning = true;
	recvThread = NULL;
	captureThread = NULL;
	decodeThread = NULL;

	qs_pool = NULL;
	qs_packet = NULL;

	DecodeCBFun = 0;
	memset(&frameInfo, 0, sizeof(FRAME_INFORMATION));

	description = (char*)malloc(1024);
	memset(description, 0, 1024);
}


VideoDecoder::~VideoDecoder(void)
{
}

bool VideoDecoder::initSocket()
{
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);

//	int RecvSocket;
	if ( (RecvSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		perror("socket");
		return false;
	}
	if (bind(RecvSocket, (struct sockaddr *)&addr, sizeof(addr)) < 0)
	{
		perror("bind");
		return false;
	}
	return true;
}

bool VideoDecoder::saveBMP(char* filename, unsigned char* RGBData, int bpp)
{
//	bool retval = false;
//	if(RGBData == NULL)
//	{
//		retval = false;
//	}
//	else
//	{
//		BITMAPFILEHEADER bmpheader;
//		BITMAPINFOHEADER bmpinfo;
//		FILE *fp = fopen(filename,"wb+");
//		if(fp == NULL)
//			return false;
//		bmpheader.bfType = 0x4d42;
//		bmpheader.bfReserved1 = 0;
//		bmpheader.bfReserved2 = 0;
//		bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//		bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;
//
//		bmpinfo.biSize = sizeof(BITMAPINFOHEADER);
//		bmpinfo.biWidth = width;
//		bmpinfo.biHeight = height;
//		bmpinfo.biPlanes = 1;
//		bmpinfo.biBitCount = bpp;
//		bmpinfo.biCompression = 0;
//		bmpinfo.biSizeImage = (width*bpp+31)/32*4*height;
//		bmpinfo.biXPelsPerMeter = 100;
//		bmpinfo.biYPelsPerMeter = 100;
//		bmpinfo.biClrUsed = 0;
//		bmpinfo.biClrImportant = 0;
//
//		fwrite (&bmpheader, sizeof(bmpheader), 1, fp);
//		fwrite (&bmpinfo, sizeof(bmpinfo), 1, fp);
//		fwrite (RGBData, width*height*bpp/8, 1, fp);
//		fclose(fp);
//		retval = true;
//	}
	return true;
}

VIDEODECODER_API bool SetDecodeCallBack(HANDLE handle, DecodeCB DecodeCBFun, void* pUser)
{
	if(DecodeCBFun == NULL )
	{
		_print_log("null DecodeCBFun.\n");
		return false;			
	}
	else if(handle == NULL)
	{
		_print_log("null handle.\n");
		return false;
	}
		DecoderHandle* handler = (DecoderHandle*)handle;

	//if(handle->decode_mode = PLAY_NORMAL_FILE)
		NormalFileDecoder* instance = (NormalFileDecoder*)handler->decoder;
	//VideoDecoder* instance = (VideoDecoder*)handle;
	instance->DecodeCBFun = DecodeCBFun;
	instance->pUser = pUser;
	return true;
}


VIDEODECODER_API HANDLE PlayNormalFile(const char* filename)
{
	_print_log("PlayNormalFile, file:%s\n", filename);
	if(access(filename, 0) != 0 && filename[0] != 'r')
	{
		_print_log("file not exists.\n");
		return NULL;			
	}
	DecoderHandle* handle = new DecoderHandle();
	handle->decode_mode = PLAY_NORMAL_FILE;
	handle->decoder = new NormalFileDecoder();
	NormalFileDecoder* decoder = (NormalFileDecoder*)handle->decoder;
	strcpy(decoder->filename, filename);

	pthread_create(&decoder->decodeThread, NULL, DecodeThread, decoder);

	int i = 0;
	for(i; i<100; i++)
	{
		if(decoder->pFormatCtx != NULL)
			break;
		else
			usleep(10);
	}
	if(i == 100) // 1000ms
	{
		delete decoder;
		delete handle;
		_print_log("Cannot open file.\n");
		return NULL;
	}
	else
		return handle;
}

VIDEODECODER_API HANDLE PlayPSFile(const char* filename)
{
	_print_log("PlayPSFile, file:%s\n", filename);
	if(access(filename, 0) != 0 )
	{
		_print_log("file not exists.\n");
		return NULL;			
	}
	DecoderHandle* handle = new DecoderHandle();
	handle->decode_mode = PLAY_PS_FILE;
	handle->decoder = new PSFileDecoder();
	PSFileDecoder* decoder = (PSFileDecoder*)handle->decoder;
	strcpy(decoder->filename, filename);
	pthread_create(&decoder->recvThread, NULL, RecvThread, decoder);
	pthread_create(&decoder->decodeThread, NULL, DecodeThread, decoder);

	return handle;
}

VIDEODECODER_API HANDLE PlayDHFile(const char* filename)
{
	_print_log("PlayDHFile, file:%s\n", filename);
	if(access(filename, 0) != 0 )
	{
		_print_log("file not exists.\n");
		return NULL;			
	}
	DecoderHandle* handle = new DecoderHandle();
//	handle->decode_mode = PLAY_DH_FILE;
//	handle->decoder = new DHFileDecoder();
//	DHFileDecoder* decoder = (DHFileDecoder*)handle->decoder;
//	strcpy(decoder->filename, filename);
//	pthread_create(&decoder->recvThread, NULL, RecvThread, decoder);
//	pthread_create(&decoder->decodeThread, NULL, DecodeThread, decoder);

	return handle;
}

VIDEODECODER_API HANDLE PlayESStream(int port)
{
	_print_log("PlayESStream, port:%d\n", port);
	DecoderHandle* handle = new DecoderHandle();
	handle->decode_mode = PLAY_ES_STREAM;
	handle->decoder = new ESDecoder();
	ESDecoder* decoder = (ESDecoder*)handle->decoder;

	decoder->port = port;
	decoder->initSocket();
	pthread_create(&decoder->recvThread, NULL, RecvThread, decoder);
	pthread_create(&decoder->decodeThread, NULL, DecodeThread, decoder);

	return handle;
}

VIDEODECODER_API HANDLE PlayPSStream(int port)
{
	_print_log("PlayPSStream, port:%d\n", port);
	DecoderHandle* handle = new DecoderHandle();
	handle->decode_mode = PLAY_PS_STREAM;
	handle->decoder = new PSDecoder();
	PSDecoder* decoder = (PSDecoder*)handle->decoder;

	decoder->port = port;
	decoder->initSocket();
	pthread_create(&decoder->recvThread, NULL, RecvThread, decoder);
	pthread_create(&decoder->decodeThread, NULL, DecodeThread, decoder);

	return handle;
}

VIDEODECODER_API HANDLE PlayDHStream(int port)
{
	_print_log("PlayDHStream, port:%d\n", port);
	DecoderHandle* handle = new DecoderHandle();
//	handle->decode_mode = PLAY_DH_STREAM;
//	handle->decoder = new DHDecoder();
//	DHDecoder* decoder = (DHDecoder*)handle->decoder;
//
//	decoder->port = port;
//	decoder->initSocket();
//	pthread_create(&decoder->recvThread, NULL, RecvThread, decoder);
//	pthread_create(&decoder->decodeThread, NULL, DecodeThread, decoder);

	return handle;
}

VIDEODECODER_API HANDLE PlayDHRTSPStream(const char* RTSPAddress)
{
	_print_log("PlayDHRTSPStream, address:%s\n", RTSPAddress);
	DecoderHandle* handle = new DecoderHandle();
//	handle->decode_mode = PLAY_DH_RTSP_STREAM;
//	handle->decoder = new DHRTSPDecoder(RTSPAddress, "");
//	DHRTSPDecoder* decoder = (DHRTSPDecoder*)handle->decoder;
//
//	pthread_create(&decoder->recvThread, NULL, RecvThread, decoder);
//	pthread_create(&decoder->decodeThread, NULL, DecodeThread, decoder);

	return handle;
}

VIDEODECODER_API HANDLE SaveDHRTSPStream(const char* RTSPAddress, const char* filename)
{
	_print_log("SaveDHRTSPStream, filename:%s, address:%s\n", filename, RTSPAddress);
	DecoderHandle* handle = new DecoderHandle();
//	handle->decode_mode = PLAY_DH_RTSP_STREAM;
//	handle->decoder = new DHRTSPDecoder(RTSPAddress, filename);
//	DHRTSPDecoder* decoder = (DHRTSPDecoder*)handle->decoder;
//
//	pthread_create(&decoder->recvThread, NULL, RecvThread, decoder);
//	pthread_create(&decoder->decodeThread, NULL, DecodeThread, decoder);

	return handle;
}

VIDEODECODER_API bool Release(HANDLE handle)
{
	DecoderHandle* decoderHandle = (DecoderHandle*)handle;
	VideoDecoder* decoder = (VideoDecoder*)decoderHandle->decoder;
	video_decoder::Stop(handle);
	if(decoder != NULL)
	{
		delete decoder;
		decoder = NULL;
		_print_log("Decoder released.\n");
		return true;
	}
	else
	{
		_print_log("Null decoder, Release failed.\n");
		return false;
	}
		
}

VIDEODECODER_API bool Play(HANDLE handle)
{
	DecoderHandle* decoderHandle = (DecoderHandle*)handle;
	VideoDecoder* decoder = (VideoDecoder*)decoderHandle->decoder;

	if(decoder == NULL)
		return false;
	decoder->playFlag = PLAY;
	_print_log("play\n");
	return true;
}

VIDEODECODER_API bool Stop(HANDLE handle)
{
	DecoderHandle* decoderHandle = (DecoderHandle*)handle;
	VideoDecoder* decoder = (VideoDecoder*)decoderHandle->decoder;

	if(decoder == NULL)
		return false;
	decoder->playFlag = STOP;
	_print_log("stop\n");
	return true;
}

VIDEODECODER_API bool Pause(HANDLE handle)
{
	DecoderHandle* decoderHandle = (DecoderHandle*)handle;
	VideoDecoder* decoder = (VideoDecoder*)decoderHandle->decoder;

	if(decoder == NULL)
		return false;
	decoder->playFlag = PAUSE;
	_print_log("Paused\n");
	return true;
}

VIDEODECODER_API bool SnapShot(HANDLE handle, char* filename)
{
	_print_log("snapshot file:%s\n", filename);
	bool retval = false;
	DecoderHandle* decoderHandle = (DecoderHandle*)handle;
	VideoDecoder* decoder = (VideoDecoder*)decoderHandle->decoder;
	if(decoder != NULL)
	{
		int bufferSize = decoder->width* decoder->height* 4;
		unsigned char* RGBBuffer = (unsigned char*)malloc(bufferSize);
		memcpy(RGBBuffer, decoder->frameBGR->data[0], bufferSize);
		// bmp start with the bottom line
		int lineSize = decoder->width*4;
		unsigned char* swapMem = (unsigned char*)malloc(lineSize);
		for(int i = 0; i< decoder->height /2;i++)
		{
			memcpy(swapMem, RGBBuffer+(i*lineSize), lineSize);
			memcpy(RGBBuffer+(i*lineSize),RGBBuffer+bufferSize-((i+1)*lineSize), lineSize);
			memcpy(RGBBuffer+bufferSize-((i+1)*lineSize),swapMem, lineSize);
		}
		retval = decoder->saveBMP(filename, RGBBuffer, 32);
		delete RGBBuffer;
		delete swapMem;
	}
	else
	{
		_print_log("null instance\n");
		retval = false;
	}
	return retval;
}

VIDEODECODER_API int GetPort(const char* ip)
{
	int iResult = 0;
	sockaddr_in RecvAddr;

	//-----------------------------------------------
	// Create a receiver socket to receive datagrams
	int RecvSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (RecvSocket == -1) {
		_print_log("socket failed with error\n");
		return false;
	}
	int i = 0;
	int port = 0;
	for(i = 0;i<10000;i++)
	{
		srand((unsigned int)(time(NULL) % 1000));

		int tmpPort = rand() + rand() + 6000;
		// Bind the socket to any address and the specified port.
		RecvAddr.sin_family = AF_INET;
		RecvAddr.sin_port = htons(tmpPort);
		RecvAddr.sin_addr.s_addr = inet_addr(ip);//htonl(INADDR_ANY);//

		iResult = bind(RecvSocket, (struct sockaddr *)&RecvAddr, sizeof (RecvAddr));
		if (iResult != 0) {
			_print_log("bind failed with error\n");
			_print_log("port %d has been occupied.\n", i);
		}
		else
		{
			close(RecvSocket);
			port = tmpPort;
			break;
		}
	}
	return port;
}

DecodeCB DecodeCBFun(char* pBuf, int size, FRAME_INFORMATION* pFrameInfo, void* pUser)
{
	printf("size:%d, width:%d, height:%d, %c\n", size, pFrameInfo->width, pFrameInfo->height, pBuf[size -1]);
	printf("current pts is %I64u\n",pFrameInfo->timeStamp);
	return 0;
}

int main()
{
	//void* instance = CreateDecoderInstance();
	int num = 3;
	
	//PlayPSFile(instance, "d://0813.hk.dat");
	//PlayNormalFile(instance, "d:/0813.hk.dat");
	//HANDLE handle = video_decoder::PlayNormalFile("rtsp://172.29.218.253:9090/dss/monitor/param?cameraid=1001231%2410&substream=1&trackID=701");
	//HANDLE handle = video_decoder::PlayNormalFile("rtsp://33.90.129.10/../110101201601131545043401/raw/1.mp4.mkv");
	 
	//HANDLE handle = video_decoder::PlayNormalFile("d:/3175_2015-12-21T12-04-00_2015-12-21T12-05-01_3175.avi");
	HANDLE handle = video_decoder::PlayPSFile("d:/16.dat");
	//HANDLE handle = video_decoder::PlayDHFile("d:/dahua.dat");
	//HANDLE handle = video_decoder::PlayDHStream(12345);
	//HANDLE handle = video_decoder::PlayDHRTSPStream("rtsp://172.29.218.253:9090/dss/monitor/param?cameraid=1001231%2410&substream=1&trackID=701");//"rtsp://172.29.218.62:5061/playback/40500");
	//HANDLE handle = video_decoder::PlayPSStream(8768);
	//HANDLE handle = video_decoder::SaveRTPStream(12345, "d:/save.dat");
	video_decoder::SetDecodeCallBack(handle, (DecodeCB)DecodeCBFun, &num);
	video_decoder::Play(handle);
	getchar();
	sleep(5);
	video_decoder::Stop(handle);
	return 0;
}
